package com.ai.bitcoin.service;

import com.ai.bitcoin.bo.MemberCenterUpdateUserBO;
import com.ai.bitcoin.constant.CodeConstant;
import com.ai.bitcoin.constant.CommonConstant;
import com.ai.bitcoin.constant.QrCodeConstant;
import com.ai.bitcoin.exception.BusinessException;
import com.ai.bitcoin.jpa.dao.*;
import com.ai.bitcoin.jpa.entity.*;
import com.ai.bitcoin.jpa.service.BccNewsService;
import com.ai.bitcoin.output.GeneralReponse;
import com.ai.bitcoin.service.helper.WeiXinServiceHelper;
import com.ai.bitcoin.service.helper.WeixinServiceUpdateHelper;
import com.ai.bitcoin.util.DateUtil;
import com.ai.bitcoin.util.JacksonUtils;
import com.ai.bitcoin.util.JwtUtil;
import com.ai.bitcoin.weixin.template.BonusChangeTemplate;
import com.ai.bitcoin.weixin.template.CoinReportTemplate;
import com.ai.bitcoin.weixin.template.SendReportService;
import com.ai.bitcoin.weixin.template.TemplateManager;
import com.google.common.collect.Lists;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.*;
import java.util.regex.Pattern;

@Service
@Slf4j
public class UserService {

    @Autowired
    MemberCenterService memberCenterService;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    CoinNews2BccDao coinNews2BccDao;

    @Autowired
    BccNewsService bccNewsService;

    @Autowired
    private WeiXinServiceHelper weiXinServiceHelper;

    @Autowired
    @Getter
    private BccUserDao bccUserDao;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private BccSubCoinDao bccSubCoinDao;

    @Autowired
    private BccSubCoinActionDao bccSubCoinActionDao;

    @Autowired
    private BccOrderDao bccOrderDao;

    @Autowired
    private BccBasicInfoDao bccBasicInfoDao;

    @Autowired
    private SendReportService sendReportService;

    @Autowired
    private WeixinServiceUpdateHelper weixinServiceUpdateHelper;

    private String PUSH_REPORT_PREFIX_URL = "https://www.valuechain.group/template/index.html?";

    public GeneralReponse generateQrcode(String ip) {
        //生成有效期10分钟的二维码
        Map<String, String> qrcode = weiXinServiceHelper.generateTemporyQrcode(Long.valueOf(10 * 60), QrCodeConstant.TEMPORY_LOGION_QRCODE_PREFIX);
        return new GeneralReponse(CodeConstant.SUCCESS, qrcode);
    }

    @Async
    public void queryIfLogin(String key, DeferredResult<String> deferredResult) {
        String data = redisTemplate.opsForValue().get("user:login:key:" + key);
        if (data == null) {
            deferredResult.setResult(JSONObject.fromObject(new GeneralReponse(CodeConstant.NO_LOGIN, null, "no login")).toString());
            return;
        }
        BccUser user = JacksonUtils.fromJson(data, BccUser.class);
        deferredResult.setResult(JSONObject.fromObject(new GeneralReponse(CodeConstant.SUCCESS, user)).toString());
    }

    public GeneralReponse info(String symbol) {
        BccUser user = bccUserDao.findBySymbol(symbol);
        return new GeneralReponse(CodeConstant.SUCCESS, user);
    }


    public GeneralReponse subrecord(String openId) {
        List<BccSubCoin> subCoins = bccSubCoinDao.queryValidByUserSymbol(openId);
        return new GeneralReponse(CodeConstant.SUCCESS, subCoins);
    }

    public GeneralReponse subaction(String symbol) {
        List<BccSubCoinAction> coinActions = bccSubCoinActionDao.queryByUserSymbol(symbol);
        return new GeneralReponse(CodeConstant.SUCCESS, coinActions);
    }

    public GeneralReponse pack(String symbol) {
        List<BccOrder> orders = bccOrderDao.findAllByStatusAndTypeAndUserSymbolOrderByUpdateTimeDesc(CommonConstant.SUCCESS_PAY, CommonConstant.ORDER_SUB_PACKAGE_TYPE, symbol);
        for (BccOrder order : orders) {
            if (order.getInfo() != null && !StringUtils.isBlank(order.getInfo())) {
                order.setContent(JacksonUtils.fromJson(order.getInfo(), BccSubPackage.class));
                order.setInfo(null);
            }
        }
        return new GeneralReponse(CodeConstant.SUCCESS, orders);
    }

    @Transactional
    public GeneralReponse sub(String symbol, String bccId, Integer time, String openId) throws BusinessException {
        BccBasicInfo info = bccBasicInfoDao.findByBccId(bccId);
        if (info == null) {
            return new GeneralReponse(CodeConstant.BUSINESS_ERROR, null, "bccid is unkonwn");
        }
        BccSubCoin subCoin = bccSubCoinDao.queryByBccIdAndUserOpenId(bccId, openId);
        if (subCoin != null && subCoin.getEndTime() > System.currentTimeMillis() / 1000L) {
            return new GeneralReponse(CodeConstant.HAS_SUB_COIN, null, "该币已订阅");
        }

        int ret = bccUserDao.descreaseUserBonus(Double.valueOf(time * 100), openId);
        if (ret == 0) {
            return new GeneralReponse(CodeConstant.BONUS_NOT_ENOUGH, null, "积分不够");
        }

        Date now = new Date();

        if (subCoin == null) {
            subCoin = new BccSubCoin();
            subCoin.setBccId(bccId);
            subCoin.setBccName(info.getNameEn());
            subCoin.setCreateTime(now);
            subCoin.setUpdateTime(now);
            subCoin.setEndTime(System.currentTimeMillis() / 1000L + time * CommonConstant.MONTH_TIME_STAMP_SECONDS);
            subCoin.setStatus(1);
            subCoin.setUserOpenId(openId);
            subCoin.setUserSymbol(symbol);
            try {
                bccSubCoinDao.save(subCoin);


                BccSubCoinAction subCoinAction = new BccSubCoinAction();
                subCoinAction.setBccId(bccId);
                subCoinAction.setCreateTime(now);
                subCoinAction.setBccName(info.getNameEn());
                subCoinAction.setType(CommonConstant.ORDER_SUB_PACKAGE_TYPE);
                subCoinAction.setUserOpenId(openId);
                subCoinAction.setValidTime(time * CommonConstant.MONTH_TIME_STAMP_SECONDS);
                bccSubCoinActionDao.save(subCoinAction);

                String bccName = info.getNameEn();
                Map<String, Object> templeMap = new HashMap<String, Object>();
                templeMap.put("openId", openId);
                templeMap.put("url", PUSH_REPORT_PREFIX_URL + "bccId=" + bccId + "T" + DateUtil.currentDate2Str(CommonConstant.REPORT_HTML_PREFIX_PATH));
                templeMap.put("first", "您有一个新币(" + bccName + ")报告已生成，点击可查看报告内容。");
                templeMap.put("keyword1", bccName);
                templeMap.put("keyword2", bccName);
                templeMap.put("remark", "感谢您的使用");
                sendReportService.sendTemplateMsg(TemplateManager.buildTemplateParater(new CoinReportTemplate(), templeMap));
            } catch (Exception e) {
                throw new BusinessException(CodeConstant.BUSINESS_ERROR, "异常回滚", e);
            }
        }
        else {
            bccSubCoinDao.updateEndTimeByBccIdAndUserSymbol(System.currentTimeMillis() / 1000L + time * CommonConstant.MONTH_TIME_STAMP_SECONDS, bccId, symbol);
            String bccName = info.getNameEn();
            Map<String, Object> templeMap = new HashMap<String, Object>();
            templeMap.put("openId", openId);
            templeMap.put("url", PUSH_REPORT_PREFIX_URL + "bccId=" + bccId + "T" + DateUtil.currentDate2Str(CommonConstant.REPORT_HTML_PREFIX_PATH));
            templeMap.put("first", "您有一个新币(" + bccName + ")报告已生成，点击可查看报告内容。");
            templeMap.put("keyword1", bccName);
            templeMap.put("keyword2", bccName);
            templeMap.put("remark", "感谢您的使用");
            sendReportService.sendTemplateMsg(TemplateManager.buildTemplateParater(new CoinReportTemplate(), templeMap));
        }

        BccUser user = bccUserDao.findByOpenId(openId);
        weixinServiceUpdateHelper.registUserPlcRecord(openId, null, "订阅" + info.getNameEn(), 3, (double) (time * 100));
        Map<String, Object> data = new HashMap<>();
        data.put("openId", openId);
        data.put("url", "http://explorelife.vip/bitcoin/bitcoin/media/" + openId + ".png");
        data.put("first", "您的PLC变更如下");
        data.put("FieldName", "消耗原因");
        data.put("Account", "订阅" + info.getNameEn());
        data.put("change", "减少");
        data.put("CreditChange", (double) (time * 100));
        data.put("CreditTotal", user.getBonus());
        sendReportService.sendTemplateMsg(TemplateManager.buildTemplateParater(new BonusChangeTemplate(), data));
        return new GeneralReponse(CodeConstant.SUCCESS, null, "订阅成功");
    }


    public void deletePlcEncourageRecord() {
        List<Integer> types = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7);
        String yesterdaydate = DateUtil.toDateStr(DateUtil.getBeginDayOfYesterday(), CommonConstant.REPORT_HTML_PREFIX_PATH);
        List<String> keys = new ArrayList<>(types.size());
        for (Integer type : types) {
            String key = "encourage:" + type + ":" + yesterdaydate;
            keys.add(key);
        }
        redisTemplate.delete(keys);
    }


    /**
     * 用户剩余奖励计算
     * 视频至计算一次 其他奖励按天计算
     * <p>
     * TODO 每个用户奖励可以用一个openId作为Key 一次读操作就可以
     *
     * @param openId        openId
     * @param userPlcEvents 奖励列表
     * @return 剩余奖励信息
     */

    public List<BccUserPlcEvent> findLeftEncourage(String openId, List<BccUserPlcEvent> userPlcEvents) {
        for (BccUserPlcEvent event : userPlcEvents) {
            String key = "encourage:" + event.getType() + ":" + DateUtil.toDateStr(DateUtil.getDayBegin(), CommonConstant.REPORT_HTML_PREFIX_PATH);
            if (event.getType() == 10) //看视频奖励
            {
                key = "encourage:" + event.getType();
            }
            Object value = redisTemplate.opsForHash().get(key, openId);
            Integer num = 0;
            if (value != null) {
                num = Integer.valueOf(String.valueOf(value));
            }
            event.setNum(event.getNum() - num);
        }
        return userPlcEvents;
    }


    /**
     * 更新用户基本细腻
     *
     * @param userBO 用户更新BO
     */
    public void updateUserInfo(MemberCenterUpdateUserBO userBO) throws BusinessException {
        if (StringUtils.isEmpty(userBO.getOpenId())) {
            throw new BusinessException(CodeConstant.BUSINESS_ERROR, "非法请求。");
        }
        BccUser user = bccUserDao.findByOpenId(userBO.getOpenId());
        if (user == null) {
            throw new BusinessException(CodeConstant.BUSINESS_ERROR, "不存在用户。");
        }

        if (StringUtils.isNotEmpty(userBO.getMobile())) {
            //验证手机号码
            String mobilePattern = "^1(?:3\\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\\d|9\\d)\\d{8}";
            boolean isMobile = Pattern.matches(mobilePattern, userBO.getMobile());
            if (!isMobile) {
                throw new BusinessException(CodeConstant.BUSINESS_ERROR, "请确认手机号码是否正确。");
            }

            if (bccUserDao.existsByMobileAndOpenIdNot(userBO.getMobile(), user.getOpenId())) {
                throw new BusinessException(CodeConstant.BUSINESS_ERROR, "手机号码已被其他账号绑定。请更换手机号码。");
            }

            user.setMobile(userBO.getMobile());
        }

        if (StringUtils.isNotEmpty(userBO.getImToken())) {
            //0x 开头 + 40位16进制字符串
            String tokenPattern = "^0x\\w{40}";
            boolean isToken = Pattern.matches(tokenPattern, userBO.getImToken());
            if (!isToken) {
                throw new BusinessException(CodeConstant.BUSINESS_ERROR, "请确认IM钱包Token是否正确。");
            }

            if (bccUserDao.existsByPurseImTokenAndOpenIdNot(userBO.getImToken(), user.getOpenId())) {
                throw new BusinessException(CodeConstant.BUSINESS_ERROR, "钱包地址已被其他账号绑定。");
            }

            user.setPurseImToken(userBO.getImToken());
        }

        if (userBO.getMonitorPushType() != null) {
            if (userBO.getMonitorPushType() > 3 || userBO.getMonitorPushType() < 1) {
                throw new BusinessException(CodeConstant.BUSINESS_ERROR, "推送时间配置错误。");
            }
            user.setMonitorPushType(userBO.getMonitorPushType());
        }
        bccUserDao.save(user);
    }
}
